// VideoImageCapDlg.cpp : implementation file
//

#include "stdafx.h"
#include "VideoImageCap.h"
#include "VideoImageCapDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX };
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVideoImageCapDlg dialog

CVideoImageCapDlg::CVideoImageCapDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CVideoImageCapDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CVideoImageCapDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	m_bCallBackCheck=FALSE;
}

void CVideoImageCapDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CVideoImageCapDlg)
	DDX_Control(pDX, IDC_EDIT_SAVE_PATH, m_strSavePath);
	DDX_Control(pDX, IDC_EDIT_STREAMING_URL, m_strStreamingUrl);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CVideoImageCapDlg, CDialog)
	//{{AFX_MSG_MAP(CVideoImageCapDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(ID_CAPTUREPIC, OnCapturePic)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CVideoImageCapDlg message handlers

BOOL CVideoImageCapDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	//Initially Set the use callback Radio Button checked
	((CButton*)GetDlgItem(IDC_RADIO_USE_CALLBACK))->SetCheck(TRUE);

	m_strSavePath.SetWindowText("Test.bmp");

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CVideoImageCapDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CVideoImageCapDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CVideoImageCapDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}


void CVideoImageCapDlg::OnCapturePic() 
{
	

	m_strStreamingUrl.GetWindowText(m_strURL);
	if(m_strURL=="")
	{
		AfxMessageBox("Please enter streaming URL");
		return;
	}
	m_strSavePath.GetWindowText(m_strSaveTo);

	if(m_strSaveTo!="")
	{
		m_FrameGrabCallback.strPath=m_strSaveTo;
	}
	m_bCallBackCheck=((CButton*)GetDlgItem(IDC_RADIO_USE_CALLBACK))->GetCheck();

	if(CaptureImage())
	{
		AfxMessageBox("Image saved successfully");
	}
	else
	{
		AfxMessageBox("Image capture failed");
	}
}
HRESULT CVideoImageCapDlg::ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pFirst, IBaseFilter *pSecond)
{
  IPin *pOut = NULL, *pIn = NULL;
  HRESULT hr = GetPin(pSecond, PINDIR_INPUT, &pIn);
  if (FAILED(hr)) return hr;
  // The previous filter may have multiple outputs, so try each one!
  IEnumPins  *pEnum;
  pFirst->EnumPins(&pEnum);
  while(pEnum->Next(1, &pOut, 0) == S_OK)
  {
     PIN_DIRECTION PinDirThis;
     pOut->QueryDirection(&PinDirThis);
     if (PINDIR_OUTPUT == PinDirThis)
     {
        hr = pGraph->Connect(pOut, pIn);
        if(!FAILED(hr))
        {
           break;
        }
     }
     pOut->Release();
  }
  pEnum->Release();
  pIn->Release();
  pOut->Release();
  return hr;
}


HRESULT CVideoImageCapDlg::GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
{
      IEnumPins  *pEnum;
      IPin       *pPin;
      pFilter->EnumPins(&pEnum);
      while(pEnum->Next(1, &pPin, 0) == S_OK)
      {
         PIN_DIRECTION PinDirThis;
         pPin->QueryDirection(&PinDirThis);
         if (PinDir == PinDirThis)
         {
            pEnum->Release();
            *ppPin = pPin;
            return S_OK;
         }
         pPin->Release();
      }
      pEnum->Release();
      return E_FAIL;  
}

BOOL CVideoImageCapDlg::CaptureImage()
{
	try
	{
		IGraphBuilder *pGraph = NULL; //Graph Manager 


		IMediaControl *pControl = NULL; //Media Control 


		IMediaEvent *pEvent = NULL; //Event Control 


		IBaseFilter *pWMASFReader = NULL; //IBase Filter for WM ASF READER (network reader) 


		IPin *pStreamOut = NULL,*pStreamRender = NULL; //IPin interfaces for connecting filters 


		IFileSourceFilter *pIFileSourceFilter = NULL; //for specifiying network target.... 


		// initialize COM, create the filter graph manager, and query for the event and control interfaces 

		HRESULT hr = CoInitialize(NULL); 

		if(FAILED(hr)) 
			return FALSE ; 


		hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
		IID_IGraphBuilder, (void **)&pGraph); 

		if(FAILED(hr)) 
			return FALSE; 

		// create the WM ASF Reader, and then setting the network location 

		hr = CoCreateInstance(CLSID_WMAsfReader,NULL,CLSCTX_INPROC_SERVER,IID_IBaseFilter, (void **) &pWMASFReader); 

		if (SUCCEEDED(hr)) 
		{ 
			hr = pGraph->AddFilter(pWMASFReader,L"WM ASF Reader"); 


			// query for the file source filter to specify the network location 

			if (SUCCEEDED(hr)) 
			{ 
				hr = pWMASFReader->QueryInterface(IID_IFileSourceFilter,(void **) 
				&pIFileSourceFilter); 

				if (SUCCEEDED(hr)) 
				{ 
					// put in your desired network source here 

					hr = pIFileSourceFilter->Load(m_strURL.AllocSysString(), NULL); 
					
				} 
			} 
		} 	

		if(FAILED(hr))
			return FALSE;

		// Create the Sample Grabber.
		IBaseFilter *pGrabberF = NULL;
		hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
			IID_IBaseFilter, (void**)&pGrabberF);
		if (FAILED(hr))
		{
			// Return an error.
			return FALSE;
		}
		hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");
		if (FAILED(hr))
		{
			// Return an error.
			return FALSE;
		}

		pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&m_pGrabber);

		AM_MEDIA_TYPE mt;
		ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
		mt.majortype = MEDIATYPE_Video;
		mt.subtype = MEDIASUBTYPE_RGB24;
		hr = m_pGrabber->SetMediaType(&mt);

		IBaseFilter	*pNullRenderer;
		hr = CoCreateInstance (CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pNullRenderer);
		hr = pGraph->AddFilter(pNullRenderer, L"Null Renderer");

		hr = ConnectFilters(pGraph, pWMASFReader, pGrabberF);


		IPin * pGrabOutPin=NULL;
		hr= GetPin( pGrabberF,PINDIR_OUTPUT,&pGrabOutPin);
		hr = pGraph->Render(pGrabOutPin);
		if( FAILED( hr ) )
		{
			AfxMessageBox("Could not render grabber output pin\r\n");
			return FALSE;
		}


		hr = pGraph->QueryInterface(IID_IMediaEvent, (void **)&pEvent); 
		if(FAILED(hr)) 
			return FALSE; 

		hr = pGraph->QueryInterface(IID_IMediaControl, (void **)&pControl); 
		if(FAILED(hr)) 
		return FALSE ; 

	//	Sleep(1000);
		// Set one-shot mode false and  buffering.
		hr = m_pGrabber->SetOneShot(FALSE);
		hr = m_pGrabber->SetBufferSamples(TRUE);

		
		if(m_bCallBackCheck)//if the callback radio button is selected
		{
			// Set the callback, so we can grab the one sample
			//Also set the video width and height to grab the frame.

			m_FrameGrabCallback.Width=320;
			m_FrameGrabCallback.Height=240;
			m_FrameGrabCallback.framenum=1;

			hr = m_pGrabber->SetCallback( &m_FrameGrabCallback, 1 );
		}

		long evCode=0;
		hr=pControl->Run(); // Run the graph.
		hr=pEvent->WaitForCompletion(INFINITE, &evCode); // Wait till its done
		Sleep(1000);

		if(!m_bCallBackCheck)
		{
			if(!(SaveBufferToBitmap(mt)))
				return FALSE;
		}

		if(m_bCallBackCheck)
		m_pGrabber->SetCallback(NULL,1);

		pControl->Stop();
		pControl->Release();
		m_pGrabber->Release();
		pEvent->Release();
		pGraph->Release();
		pGrabberF->Release();
		//pNullRenderer->Release();

		return TRUE;
	}
	catch(...)
	{
		return FALSE;
	}
	
}

BOOL CVideoImageCapDlg::SaveBufferToBitmap(AM_MEDIA_TYPE mt)
{
		// Find the required buffer size.
	long cbBuffer = 0;
	HRESULT hr = m_pGrabber->GetCurrentBuffer(&cbBuffer, NULL);

	switch(hr)
	{
		case E_INVALIDARG:
			AfxMessageBox("E_INVALIDARG");
			break;

		case E_OUTOFMEMORY:
			AfxMessageBox("E_OUTOFMEMORY");
			break;

		case E_POINTER:
			AfxMessageBox("E_POINTER");
			break;

		case VFW_E_NOT_CONNECTED:
			AfxMessageBox("VFW_E_NOT_CONNECTED");
			break;

		case VFW_E_WRONG_STATE:
			AfxMessageBox("VFW_E_WRONG_STATE");
			break;
	}

	if(FAILED(hr))
		return FALSE;

	char *pBuffer = new char[cbBuffer];
	if (!pBuffer) 
	{
		// Out of memory. Return an error code.
		return FALSE;
	}
	hr = m_pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);


	ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
	hr = m_pGrabber->GetConnectedMediaType(&mt);
	if (FAILED(hr))
	{
		// Return error code.
		return FALSE;
	}
	// Examine the format block.
	VIDEOINFOHEADER *pVih;
	if ((mt.formattype == FORMAT_VideoInfo) && 
		(mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
		(mt.pbFormat != NULL) ) 
	{
		pVih = (VIDEOINFOHEADER*)mt.pbFormat;
	}
	else 
	{
		return FALSE;
	}

	 //
	 // Save image data as Bitmap.
	 // This is just to make this sample easily understandable.
	 //
	 HANDLE fh;
	 BITMAPFILEHEADER bmphdr;
	 DWORD nWritten;

	 memset(&bmphdr, 0, sizeof(bmphdr));

	 bmphdr.bfType = ('M' << 8) | 'B';
	 bmphdr.bfSize = sizeof(bmphdr) + sizeof(BITMAPINFOHEADER) + cbBuffer;
	 bmphdr.bfOffBits = sizeof(bmphdr) + sizeof(BITMAPINFOHEADER);

	 fh = CreateFile(m_strSaveTo,
		 GENERIC_WRITE, 0, NULL,
		 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	 WriteFile(fh, &bmphdr, sizeof(bmphdr), &nWritten, NULL);
	 WriteFile(fh,
		 &pVih->bmiHeader,
		 sizeof(BITMAPINFOHEADER), &nWritten, NULL);
	 WriteFile(fh, pBuffer, cbBuffer, &nWritten, NULL);
	 CloseHandle(fh);

	 free(pBuffer);

	 return TRUE;
}
